﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;
using System.Xml.Linq;
using System.Dynamic;
using System.IO;
using System.Collections.Concurrent;
using System.Threading;
using System.Diagnostics.Contracts;
using System.Collections;
using System.Reflection;
using System.Diagnostics;
using System.Xml;

using King.Extensions;
using King.Text;
using King.IO;
using King.Loader;
using King.Reflection;
using King.Collections;
using King.CSharp.Reflection;
using King.Xml.IO;
using King.Xml.Reflection;
using King.Xml.Reflection.Emit;
using King.Xml.CSharp;
using King.Xml.CSharp.Reflection;
using King.Xml.CSharp.Reflection.Emit;
using King.Rest;
using King.Rest.Xml.CSharp;
using King.Rest.Reflection;
using King.Rest.Reflection.Emit;
using King.Rest.Xml.CSharp.Reflection;

namespace King.Rest.Reflection {

    public class RestXmlLoaderException : Exception {
        internal RestXmlLoaderException(string message)
            : base(message) {
        }
    }

    internal sealed class RestXmlXsdGenerator {
        private static readonly XNamespace Xs = "http://www.w3.org/2001/XMLSchema";

        private RestXmlSchema m_schema;
        private Dictionary<XNamespace, string> m_prefixByXNamespace;

        internal RestXmlXsdGenerator(RestXmlSchema schema) {
            m_schema = schema;
            m_prefixByXNamespace = new Dictionary<XNamespace, string>();
            m_prefixByXNamespace[Xs] = "xs";
        }

        internal XDocument GenerateDocument() {
            var doc = new XDocument(
                new XDeclaration("1.0", "uft-8", "yes"),
                new XElement(Xs + "schema",
                    new XAttribute(XNamespace.Xmlns + "xs", Xs),
                    new XAttribute("elementFormDefault", "qualified"),

                    m_schema.Messages().Select(o => GenerateElementProperty(o)),

                    m_schema.Types().Where(o => o.IsSimpleType).Select(o => GenerateSimpleType(o)),

                    m_schema.Types().Where(o => o.IsComplexType).Select(o => GenerateComplexType(o))
                )
            );

            return doc;
        }

        private object GeneratePropertyType(RestXmlType type) {
            if (type.IsObject)
                return new XElement(Xs + "complexType",
                    new XElement(Xs + "sequence",
                        new XElement(Xs + "any")
                    )
                );

            if (type.IsList)
                return new XElement(Xs + "complexType",
                    new XElement(Xs + "sequence",
                        new XElement(Xs + "any"),
                        new XAttribute("minOccurs", "0"),
                        new XAttribute("maxOccurs", "unbounded")
                    )
                );

            return new XAttribute("type", type.XName);
        }
        private XElement GenerateElementProperty(RestXmlProperty property) {
            return new XElement(Xs + "element",
                new XAttribute("name", property.XName),
                GeneratePropertyType(property.PropertyType),
                property.IsOptional ? new XAttribute("minOccurs", "0") : null
            );
        }
        private string GenerateQName(XName name) {
            if (name.Namespace == XNamespace.None)
                return name.LocalName;

            if (name.Namespace == m_schema.XNamespace)
                return name.LocalName;

            var ns = m_prefixByXNamespace[name.Namespace];
            return ns + ":" + name.LocalName;
        }
        private XElement GenerateSimpleType(RestXmlType type) {
            return new XElement(Xs + "simpleType",
                new XAttribute("name", type.XName),
                new XElement(Xs + "restriction",
                    new XAttribute("base", GenerateQName(type.XsType.BaseType.XName))
                )
            );

        }
        private XElement GenerateComplexType(RestXmlType type) {
            return new XElement(Xs + "complexType",
                new XAttribute("name", type.XName),
                type.Elements().None() ? null : new XElement(Xs + "sequence",
                    type.Elements().Select(o => GenerateElementProperty(o))
                ),
                type.Attributes().Select(o => GenerateAttributeProperty(o))
            );
        }
        private XElement GenerateAttributeProperty(RestXmlProperty property) {
            return new XElement(Xs + "attribute",
                new XAttribute("name", property.XName),
                new XAttribute("type", property.PropertyType.XName)
            );
        }
    }

    public sealed class RestXmlLoader {
        public const string RestNamespace = "http://www.king.com/2012/restSchema";
        public const string RestDefaultPrefix = "rs";
        public static readonly XPrefix RestDefaultXPrefix = (XPrefix)RestDefaultPrefix;
        public static readonly XNamespace RestXNamespace = (XNamespace)RestNamespace;

        internal static readonly XName AnyXName = RestXNamespace + "any";
        internal static readonly XName ListXName = RestXNamespace + "list";

        internal static readonly XDocument RestXsdXDocument = XDocument.Parse(
            new StreamReader(
                Assembly.GetExecutingAssembly()
                    .GetManifestResourceStream("King.Web.rest.xsd")
            ).ReadToEnd()
        );

        internal static Exception ThrowUnexpectedXsConstruction() {
            throw new ArgumentException("Unsupported xsd construction encountered.");
        }

        private readonly XsLoader m_xsLoader;
        private readonly Loader<XsSchema, RestXmlSchema> m_schemaLoader;
        private readonly Func<object, RestXmlType> m_getType;
        private readonly Func<object, RestXmlSchema> m_getSchema;

        public RestXmlLoader(
            XsLoader xsLoader = null,
            Func<RestXmlSchemaFactory, RestXmlSchema> schemaFactory = null,
            Func<object, RestXmlType> getType = null,
            Func<object, RestXmlSchema> getSchema = null) {

            m_getType = getType;
            m_getSchema = getSchema;
            
            if (schemaFactory == null)
                schemaFactory = o => o.CreateSchema();

            // teach XsLoader how to load RestXNamespace
            m_xsLoader = xsLoader ?? new XsLoader(o => {

                if (o.XNamespace == RestXmlLoader.RestXNamespace)
                    return o.CreateSchema(RestXsdXDocument);
                
                return null;
            });

            m_schemaLoader = new Loader<XsSchema, RestXmlSchema>(
                keys: null,
                defaultResolver: o => schemaFactory(new RestXmlSchemaFactory(this, o.Key)),
                getLoader: o => o.Loader.m_schemaLoader,
                getKeys: o => new object[] { 
                    o.XsSchema,
                },
                normalizeKey: o => {

                    // XsSchema
                    var xsSchema = o as XsSchema;
                    if (xsSchema == null)
                        return null;

                    return xsSchema;
                }
            );
        }

        internal RestXmlType GetType(object value) {
            if (value == null)
                return null;

            if (m_getType == null)
                throw new InvalidOperationException();

            return m_getType(value);
        }
        internal RestXmlSchema GetSchema(object value) {
            if (value == null)
                return null;

            if (m_getSchema == null)
                throw new InvalidOperationException();

            return m_getSchema(value);
        }

        public XsLoader XsLoader {
            get { return m_xsLoader; }
        }
        public RestXmlType Any {
            get { return GetType(AnyXName); }
        }
        public RestXmlType List {
            get { return GetType(ListXName); }
        }

        public RestXmlSchema GetSchema(XsSchema schema) {
            return m_schemaLoader.TryGet(schema);
        }
        public RestXmlSchema GetSchema(XNamespace xNamespace) {
            var xsSchema = XsLoader.GetSchema(xNamespace);
            return GetSchema(xsSchema);
        }

        public RestXmlType GetType(XName name) {
            if (name == null)
                return null;

            var schema = GetSchema(name.Namespace);
            if (schema == null)
                return null;

            return schema.GetType(name);
        }
        public RestXmlType GetType(XsType xsType) {
            if (xsType == null)
                return null;

            var schema = GetSchema(xsType.Schema);
            if (schema == null)
                return null;

            return schema.GetType(xsType);
        }

        public Task<dynamic> Deserialize(string xml) {
            return Deserialize(new StringReader(xml));
        }
        public async Task<dynamic> Deserialize(TextReader stream) {

            if (stream == null)
                throw new ArgumentNullException();

            // create validating xml stream reader
            var xmlStream = new XStreamReader(stream);

            // create deserializer (stack; grammar)
            var deserializer = new RestXmlDeserializer(this, xmlStream);

            // deserialize until the first async property is encountered
            var result = await deserializer.Deserialize();

            // return partially deserialized object
            return result;
        }

        public string Serialize(object value) {
            var stringBuilder = new StringBuilder();
            var xmlWriterSettings = new XmlWriterSettings() {
                OmitXmlDeclaration = true,
                Indent = true,
                Async = true
            };

            using (var xmlWriter = XmlWriter.Create(stringBuilder, xmlWriterSettings))
                Serialize(value, xmlWriter).Wait();

            return stringBuilder.ToString();
        }
        public async Task Serialize(object value, XmlWriter xmlWriter) {
            if (value == null)
                return;

            // resolve type and schema
            var schema = GetSchema(value);
            var type = GetType(value);

            if (schema == null && type == null)
                throw new ArgumentException();

            if (schema == null)
                schema = type.Schema;

            // xmlObject
            var xmlObject = new RestXmlValue(
                schema,
                type,
                value
            );

            // serializer
            var serializer = new RestXmlSerializer(xmlWriter, schema);

            // schema
            if (type == null)
                await serializer.SerializeProperties(xmlObject);

            // type
            else
                await serializer.SerializeObject(xmlObject);

        }
    }
    public sealed class RestXmlSchema : RestXmlLoaderMember {

        private readonly RestXmlLoader m_loader;
        private readonly XsSchema m_xsSchema;
        private readonly Loader<XName, RestXmlType> m_typeLoader;
        private readonly Loader<XName, RestXmlProperty> m_messageLoader;
        private readonly Func<object> m_activate;

        internal RestXmlSchema(
            RestXmlLoader loader,
            XsSchema xsSchema,
            Func<RestXmlTypeFactory, RestXmlType> typeFactory,
            Func<object> activate) : base(loader) {

            m_loader = loader;
            m_xsSchema = xsSchema;
            m_activate = activate;
            
            // user supplied metadata
            if (typeFactory == null)
                typeFactory = o => {

                    // simple
                    if (o.XsType.IsSimpleType)
                        return o.CreateSimpleType();

                    // complex
                    return o.CreateComplexType();
                };

            m_messageLoader = new Loader<XName, RestXmlProperty>(
                keys: o => xsSchema.RootElements().Select(x => x.XName),
                defaultResolver: o => {
                    var xName = o.Key;
                    var rootElement = xsSchema.GetRootElement(xName);
                    if (rootElement == null)
                        return null;

                    return new RestXmlProperty(
                        this, 
                        rootElement.XName,
                        rootElement, 
                        o.Order);
                },
                getLoader: o => o.Schema.m_messageLoader,
                getKeys: o => new object[] {
                    o.XName,
                    o.Name,
                    o.XsElement
                },
                normalizeKey: o => {
                    var xName = o as XName;
                    if (xName == null) {

                        var xsElement = o as XsElement;
                        if (xsElement != null)
                            return xsElement.XName;

                        var name = o as string;
                        if (name == null)
                            return null;

                        xName = XNamespace + name;
                    }
                    return xName;
                }
            );

            m_typeLoader = new Loader<XName, RestXmlType>(
                keys: o => xsSchema.Types(),
                normalizeKey: o => {
                    var xName = o as XName;
                    if (xName == null) {

                        var name = o as string;
                        if (name == null)
                            return null;

                        xName = XNamespace + name;
                    }

                    if (xName.Namespace != XNamespace)
                        return null;

                    return xName;
                },
                defaultResolver: o => {
                    var xName = o.Key;
                   
                    var xsType = XsSchema.GetType(xName);
                    if (xsType == null)
                        return null;

                    return typeFactory(new RestXmlTypeFactory(this, xsType));
                },
                getLoader: o => o.Schema.m_typeLoader,
                getKeys: o => new object[] { 
                    o.XName,
                    o.Name
                }
            );
        }
        
        internal object Activate() {
            if (m_activate == null)
                throw new InvalidOperationException();
            return m_activate();
        }

        public override XsObject XsObject {
            get { return m_xsSchema; }
        }

        public XsSchema XsSchema {
            get { return m_xsSchema; }
        }
        public XNamespace XNamespace {
            get { return XsSchema.XNamespace; }
        }

        public IEnumerable<XPrefix> Prefixes() {
            return XsSchema.Prefixes();
        }
        public IEnumerable<XPrefix> Prefixes(RestXmlSchema schema) {
            return XsSchema.Prefixes(schema.XsSchema);
        }
        public IEnumerable<XPrefix> Prefixes(XNamespace xNamespace) {
            return XsSchema.Prefixes(xNamespace);
        }

        public XQName GetQName(XName xName) {
            return XsSchema.GetQName(xName);
        }
        public XQName GetQName(XsType xsType) {
            return XsSchema.GetQName(xsType);
        }
        public XQName GetQName(RestXmlType restXmlType) {
            return XsSchema.GetQName(restXmlType.XsType);
        }
        public XsSchema GetSchema(XPrefix prefix) {
            return XsSchema.GetSchema(prefix);
        }
        public XNamespace GetXNamespace(XPrefix prefix) {
            return XsSchema.GetXNamespace(prefix);
        }

        public IEnumerable<RestXmlType> Types() {
            return m_typeLoader.Values();
        }
        public RestXmlType GetType(XsType type) {
            return m_typeLoader.TryGet(type.XName);
        }
        public RestXmlType GetType(XQName qName) {
            return m_typeLoader.TryGet(XsSchema.GetXName(qName));
        }
        public RestXmlType GetType(string name) {
            return m_typeLoader.TryGet(name);
        }
        public RestXmlType GetType(XName xName) {
            return m_typeLoader.TryGet(xName);
        }

        public IEnumerable<RestXmlProperty> Messages() {
            return m_messageLoader.Values();
        }
        public RestXmlProperty GetMessage(string name) {
            return m_messageLoader.TryGet(name);
        }
        public RestXmlProperty GetMessage(XName xName) {
            return m_messageLoader.TryGet(xName);
        }
        public RestXmlProperty GetMessage(XsElement xsElement) {
            return m_messageLoader.TryGet(xsElement);
        }

        public override string ToString() {
            if (XNamespace == XNamespace.None)
                return "[XNamespace.None]";
            return XNamespace.ToString();
        }
    }

    public abstract class RestXmlLoaderMember {
        private RestXmlLoader m_loader;
        private Lazy<IEnumerable<object>> m_appInfo;
        private Lazy<string> m_documentation;
        private Lazy<string> m_comment;

        internal RestXmlLoaderMember(
            RestXmlLoader loader) {
            m_loader = loader;

            m_documentation = new Lazy<string>(() => (string)XsObject.Documentation);
            m_comment = new Lazy<string>(() => XsObject.Comment);
            m_appInfo = new Lazy<IEnumerable<object>>(() => {
                var xElement = XsObject.AppInfo;
                if (xElement == null)
                    return Enumerable.Empty<object>();

                // rename the root node to be in the Rest namespace
                var appInfo = new XElement(RestXmlLoader.RestXNamespace + "appInfo", xElement.Elements());

                // deserialize attributes
                var rest = Loader.GetSchema(RestXmlLoader.RestXNamespace);
                var message = Loader.Deserialize(new StringReader((string)xElement)).Result;

                // return result
                return message.AppInfo;
            });
        }

        public abstract XsObject XsObject {
            get;
        }
        public RestXmlLoader Loader {
            get { return m_loader; }
        }
        public XsLoader XsLoader {
            get { return Loader.XsLoader; }
        }

        public IEnumerable<dynamic> AppInfo(RestXmlType type = null) {
            IEnumerable<object> attributes = m_appInfo.Value;

            return Enumerable.Empty<object>();

            //if (type != null)
            //    attributes = attributes.Where(o => o.Type.IsOrIsSubclassOf(type));

            //return attributes;
        }
        public string Documentation {
            get { return m_documentation.Value; }
        }
        public string Comment {
            get { return m_comment.Value; }
        }
    }
    public abstract class RestXmlSchemaMember : RestXmlLoaderMember {
        private RestXmlSchema m_schema;

        internal RestXmlSchemaMember(
            RestXmlSchema schema
        ) : base(schema.Loader) {
            m_schema = schema;
        }

        public RestXmlSchema Schema {
            get { return m_schema; }
        }
        public XsSchema XsSchema {
            get { return Schema.XsSchema; }
        }
        public XNamespace XNamespace {
            get { return XsSchema.XNamespace; }
        }
    }

    public abstract class RestXmlType : RestXmlSchemaMember {
        private readonly XsType m_xsType;

        internal RestXmlType(
            RestXmlSchema schema,
            XsType xsType
        ) : base(schema) {
            m_xsType = xsType;
        }

        internal virtual object Activate() {
            throw new InvalidOperationException();
        }

        public override XsObject XsObject {
            get { return XsLoader.GetType(XName); }
        }
        public RestXmlType BaseType {
            get {
                var baseType = Loader.GetType(XsType.BaseType);
                if (baseType != null)
                    return baseType;
                
                return Loader.Any; 
            }
        }
        public bool IsOrIsSubclassOf(RestXmlType type) {
            if (this == type)
                return true;

            if (BaseType == null)
                return false;

            return BaseType == type;
        }

        public XName XName {
            get { return m_xsType.XName; }
        }
        public string Name {
            get { return XName.LocalName; } 
        }

        // simple type
        public virtual bool IsSimpleType {
            get { return false; }
        }
        public virtual XsType XsType {
            get { return m_xsType; }
        }

        // complex type
        public virtual bool IsComplexType {
            get { return false; }
        }
        public IEnumerable<RestXmlProperty> Attributes() {
            return Properties().Where(o => o.IsAttribute);
        }
        public IEnumerable<RestXmlProperty> Elements() {
            return Properties().Where(o => !o.IsAttribute);
        }

        public virtual RestXmlProperty ContentProperty {
            get { return null; }
        }
        public virtual IEnumerable<RestXmlProperty> Properties() {
            return Enumerable.Empty<RestXmlProperty>();
        }
        public virtual RestXmlProperty GetProperty(string name) {
            return null;
        }
        public virtual RestXmlProperty GetProperty(XName xName) {
            return null;
        }
        public virtual RestXmlProperty GetProperty(XsElement xsElement) {
            return null;
        }
        public virtual RestXmlProperty GetProperty(XsAttribute xsAttribute) {
            return null;
        }

        // list
        public virtual bool IsList {
            get { return false; }
        }
        public virtual bool IsObject {
            get { return false; }
        }
        public IEnumerable<RestXmlType> Choices() {
            return Enumerable.Empty<RestXmlType>();
        }
        public RestXmlType SetChoices(params RestXmlType[] choices) {
            throw new InvalidOperationException();
        }

        public virtual object Deserialize(string value) {
            throw new InvalidOperationException();
        }
        public virtual string Serialize(object value) {
            throw new InvalidOperationException();
        }

        public override string ToString() {
            return XName.ToString();
        }
    }
    internal sealed class RestXmlSimpleType : RestXmlType {
        private Func<string, object> m_deserialize;
        private Func<object, string> m_serialize;

        public RestXmlSimpleType(
            RestXmlSchema schema,
            XsType xsType,
            Func<string, object> deserialize = null,
            Func<object, string> serialize = null
        ) : base(schema, xsType) {
            m_deserialize = deserialize;
            m_serialize = serialize;
        }

        public override bool IsSimpleType {
            get { return true; }
        }
        public override object Deserialize(string value) {
            if (m_deserialize != null)
                return m_deserialize(value);
            return XsType.Deserialize(value);
        }
        public override string Serialize(object value) {
            if (m_serialize != null)
                return m_serialize(value);
            return XsType.Serialize(value);
        }
    }
    internal sealed class RestXmlComplexType : RestXmlType {

        private static IEnumerable<XsElement> Elements(XsComplexContent complexContent) {

            if (complexContent == null)
                return Enumerable.Empty<XsElement>();

            // element
            if (complexContent is XsElement) {
                var xsElement = complexContent as XsElement;

                // suppress prohibited elements of restricted derivations
                if (xsElement.MaxOccurs == 0)
                    return Enumerable.Empty<XsElement>();

                return new[] { xsElement };
            }

            // sequence
            if (complexContent is XsSequence)
                return ((XsSequence)complexContent).Content().SelectMany(o => Elements(o));

            // any
            // all
            // choice

            throw RestXmlLoader.ThrowUnexpectedXsConstruction();
        }

        private readonly Lazy<RestXmlProperty> m_contentProperty;
        private readonly Loader<XsObject, RestXmlProperty> m_propertyLoader;
        private readonly Func<object> m_activate;

        internal RestXmlComplexType(
            RestXmlSchema schema,
            XsType xsType,
            Func<object> activate,
            Func<RestXmlPropertyFactory, RestXmlProperty> propertyFactory
        ) : base(schema, xsType) {

            m_activate = activate;

            // extract name from element or attribute
            Func<XsObject, string> getName = o => { 
                if (o is XsElement)
                    return ((XsElement)o).Name;
              
                if (o is XsAttribute)
                    return ((XsAttribute)o).Name;

                return null;
            };

            // attributes, simple/complex content => properties
            var properties = new Lazy<IEnumerable<XsObject>>(() => {

                // attributes
                var attributes = XsType.Attributes()
                    .Where(o => o.Use != XsAttributeUse.Prohibitied)
                    .Cast<XsObject>();

                // complex content
                var complexContent = Elements(XsType.ComplexContent)
                    .Cast<XsObject>();

                // concat
                return attributes.Concat(complexContent);
            });

            m_contentProperty = new Lazy<RestXmlProperty>(() => {
                if (xsType.SimpleContent == null)
                    return null;

                return propertyFactory(
                    new RestXmlPropertyFactory(
                        schema: schema, 
                        declaringType: this,
                        xsObject: xsType.SimpleContent
                    )
                );
            });

            // properties by name
            var propertyByName = new Lazy<Dictionary<string, XsObject>>(() => 
                properties.Value.ToDictionary(o => getName(o), o => o));

            // properties by order
            var propertyNames = new Lazy<List<string>>(() =>
                properties.Value.ToList(o => getName(o)));

            m_propertyLoader = new Loader<XsObject, RestXmlProperty>(
                keys: o => propertyNames.Value,
                defaultResolver: o => propertyFactory(
                    new RestXmlPropertyFactory(
                        schema: Schema, 
                        xsObject: o.Key, 
                        order: o.Order, 
                        declaringType: this 
                    )
                ),
                getLoader: o => ((RestXmlComplexType)o.DeclaringType).m_propertyLoader,
                getKeys: o => new object[] {
                    o.XName,
                    o.Name,
                    o.XsAttribute,
                    o.XsElement
                },
                normalizeKey: o => {

                    var xsObject = o as XsObject;
                    if (xsObject == null) {
                    
                        var name = XsResolver.ResolveName(o, XNamespace);
                        if (name == null)
                            return null;

                        xsObject = propertyByName.Value.GetValueOrDefault(name);
                    }

                    return xsObject;
                }
            );
        }

        internal override object Activate() {
            if (m_activate == null)
                throw new InvalidOperationException();
            return m_activate();
        }

        public override bool IsComplexType {
            get { return true; }
        }
        public override bool IsList {
            get { return XName == RestXmlLoader.ListXName; }
        }
        public override bool IsObject {
            get { return XName == RestXmlLoader.AnyXName; }
        }

        public override RestXmlProperty ContentProperty {
            get { return m_contentProperty.Value; }
        }
        public override IEnumerable<RestXmlProperty> Properties() {
            var result = m_propertyLoader.Values();
            if (ContentProperty != null)
                result = result.Concat(ContentProperty);
            return result;
        }
        public override RestXmlProperty GetProperty(string name) {
            return m_propertyLoader.TryGet(name);
        }
        public override RestXmlProperty GetProperty(XName xName) {
            return m_propertyLoader.TryGet(xName);
        }
        public override RestXmlProperty GetProperty(XsElement xsElement) {
            return m_propertyLoader.TryGet(xsElement);
        }
        public override RestXmlProperty GetProperty(XsAttribute xsAttribute) {
            return m_propertyLoader.TryGet(xsAttribute);
        }
    }

    public sealed class RestXmlProperty : RestXmlSchemaMember {
        private readonly RestXmlType m_declaringType;
        private readonly XsObject m_xsObject;
        private readonly XName m_xName;
        private readonly Func<object, object> m_getValue;
        private readonly Action<object, object> m_setValue;
        private readonly int? m_order;
        private readonly Lazy<bool> m_isOptional;
        private readonly Lazy<bool> m_isStreamable;
        private readonly Lazy<bool> m_isCData;
        private readonly Lazy<RestXmlType> m_propertyType;

        internal RestXmlProperty(
            RestXmlSchema schema,
            XName xName,
            XsObject xsObject,
            int? order,
            Func<RestXmlTypeFactory, RestXmlType> simpleContent = null,
            RestXmlType declaringType = null,
            Func<object, object> getValue = null,
            Action<object, object> setValue = null
        ) : base(schema) {

            m_declaringType = declaringType;
            m_xsObject = xsObject;
            m_order = order;
            m_getValue = getValue;
            m_setValue = setValue;
            m_xName = xName;

            m_isOptional = new Lazy<bool>(() =>
                IsElement ?
                    XsElement.MinOccurs == 0 :
                    XsAttribute.Use == XsAttributeUse.Optional
            );

            m_isStreamable = new Lazy<bool>(() => {
                var rest = Loader.GetSchema(RestXmlLoader.RestXNamespace);
                var propertyAppInfoType = rest.GetType("propertyAppInfo");
                var appInfo = AppInfo(propertyAppInfoType).SingleOrDefault();
                if (appInfo == null)
                    return false;

                return appInfo.isStreamable ?? false;
            });

            m_isCData = new Lazy<bool>(() => false);

            m_propertyType = new Lazy<RestXmlType>(() => {
                XsType xsType = null;

                // content property type
                if (IsContent) {

                    // content type must be simple or Any or List
                    xsType = (XsType)m_xsObject;

                    if (xsType == Loader.List.XsType)
                        return Loader.List;

                    if (xsType == Loader.Any.XsType)
                        return Loader.Any;

                    ASSERT.Condition(xsType.IsSimpleType);
                    if (xsType.XName == null)
                        return simpleContent(new RestXmlTypeFactory(schema, xsType));

                    return Loader.GetType(xsType);

                    //throw new RestXmlCSharpLoaderException(
                    //    ("Content property '{0}' on type '{1}' cannot return " +
                    //    "a complex type except for Rs.Any and Rs.List")
                    //    .Substitute(Name, DeclaringType));

                } 

                // general property type
                else {
                    ASSERT.Condition(IsAttribute || IsElement);
                    
                    if (IsElement)
                        xsType = XsElement.Type;

                    else
                        xsType = XsAttribute.Type;

                    var type = Loader.GetType(xsType);
                    ASSERT.Condition(type != null);

                    //if (type == null)
                    //    throw new RestXmlLoaderException(
                    //        "RestXmlType '{0}' of property '{1}' of XsType '{2}' failed to load."
                    //        .Substitute(DeclaringType, Name, xsType));

                    return type;
                }
            });
        }

        internal int? Order {
            get { return m_order; }
        }

        public override XsObject XsObject {
            get { return m_xsObject; }
        }

        public RestXmlType DeclaringType {
            get { return m_declaringType; }
        }
        public string Name {
            get {
                if (XName == null)
                    return null;
                return XName.LocalName; 
            }
        }
        public XName XName {
            get { return m_xName; }
        }

        public XsElement XsElement {
            get { return m_xsObject as XsElement; }
        }
        public XsAttribute XsAttribute {
            get { return m_xsObject as XsAttribute; }
        }
        public bool IsElement {
            get { return XsElement != null; }
        }
        public bool IsAttribute {
            get { return XsAttribute != null; }
        }
        public bool IsContent {
            get { return m_xsObject is XsType; }
        }

        public bool IsOptional {
            get { return m_isOptional.Value; }
        }
        public bool IsStreamable {
            get { return m_isStreamable.Value; }
        }
        public bool IsCData {
            get { return m_isCData.Value; }
        }
        public RestXmlType PropertyType {
            get { return m_propertyType.Value; }
        }
        public object DefaultValue {
            get { throw new NotImplementedException(); }
        }

        public void SetValue(object target, object value) {
            if (m_setValue == null)
                throw new InvalidOperationException();

            m_setValue(target, value);
        }
        public object GetValue(object target) {
            if (m_getValue == null)
                throw new InvalidOperationException();

            return m_getValue(target);
        }

        public override string ToString() {
            return Name;
        }
    }

    /// <summary>
    /// This is a streaming XML de/serializer with a type system common to REST APIs. The type system 
    /// has four main concepts: (1) ENTITY, (2) PROPERTY, (3) LIST and (4) VALUE. ENTITIES contain
    /// PROPERTIES each of which may contain a VALUE, LIST, or another ENTITY. LISTS come in two 
    /// flavors: SINGLETONS which must contain a single ENTITY or UNBOUNDED which may contain many 
    /// ENTITIES or NONE. A CLR object can be mapped to either a SINGLETON or UNBOUNDED list
    /// depending on attribution. 
    /// 
    /// A VALUE is represented by text which can be either the value of an attribute or the
    /// text child of an element. A VALUE has a TYPE which can be either a string, int, long, double, 
    /// bool, guid, enum, or date. These are the set of PRIMITIVE types.
    /// 
    /// An ENTITY is represented by an element. The element contains a set of attributes and/or a 
    /// list of element children. Elements and attributes all have unique names and each of them represent
    /// a PROPERTY. The set of PROPERTIES and how they are represented compose the ENTITIES type.
    /// 
    /// ENTITIES can be stored in PROPERTIES or LISTS. If the ENTITY it stored in a PROPERTY than the 
    /// element name is the PROPERTY name. If the ENTITY is stored in a LIST then the element name is 
    /// the name of the ENTITY's type. 
    /// 
    /// PROPERTIES are typed name/value pairs. A PROPERTY may contain a VALUE, an ENTITY, or LIST. 
    /// The type can be a PRIMITIVE, a specific type of ENTITY, or a LIST (SINGLETON or UNBOUNDED).
    /// If a PROPERTY is typed as a PRIMITIVE than it is a TERMINAL PROPERTY else it is a NON-TERMINAL PROPERTY. 
    /// A TERMINAL PROPERTY can be represented by either an attribute or an element with a single text child. 
    /// NON-TERMINAL PROPERTIES are always represented by elements. 
    /// 
    /// If the PROPERTY has an ENTITY type than the element doubles as the root of the ENTITY and its child 
    /// elements and attributes are then the PROPERTIES of that type. If the PROPERTY is a SINGLETON then the 
    /// element representing the property has a single child element. That child element's name maps to the 
    /// type of an ENTITY and it's child elements and attributes are then the properties of the entity. If the
    /// PROPERTY is a LIST then it's represented like a SINGLETON except there can be more than one child element.
    /// LISTS of LISTS are not supported.
    /// 
    /// Note that this type system does not support mapping to a type system (like C#) that supports upcasting
    /// to a UDT. That is to say an ENTITY can only be stored in a property of its TYPE or in a LIST. 
    /// So an ENTITY of type "car" can be stored in a property of type "car" but not of type "vehicle".
    ///  
    /// A LIST is an element with element children. The element name is that of the PROPERTY
    /// in which it is stored. Each child's name is the name type of the ENTITY. The elements stored in a LIST
    /// need not all be of the same ENTITY type.
    /// 
    /// An ENTITY is deserialized into a DynamicObject with a property for every PROPERTY. 
    /// A PROPERTY can be cast to a PRIMITIVE (e.g. int, string, bool, etc) or IEnumerable. A property can
    /// also be cast to IEnumerable<RestAttribute> to get metadata for that PROPERTY. SINGLETONS are 
    /// unwrapped. That is to say that a SINGLETON is deserialized as a DynamicObject not as an enumeration 
    /// of a single DynamicObject.
    /// 
    /// (<root>)
    ///     <rootEntityPropertyFoo stringValueProperty0="hi" intValueProperty1="10">
    ///         <stringValueProperty2>propertyValue</stringValueProperty2>
    ///         <entityValueProperty3 ...attributes...>
    ///             ...elements...
    ///         </entityValueProperty3>
    ///         <singletonListProperty4>
    ///             <entityElementType ...attributes...>
    ///                 ...elements...
    ///             </entityElementType>
    ///         </singletonListProperty4>
    ///         <unboundedListProperty5>
    ///             <entityElementType ...attributes...>
    ///                 ...elements...
    ///             </entityElementType>
    ///             <entityElementType ...attributes...>
    ///                 ...elements...
    ///             </entityElementType>
    ///         </unboundedListProperty5>
    ///     </rootEntityPropertyFoo> 
    /// (</root>)
    ///     
    /// 
    /// </summary>
    internal class RestXmlDeserializer {

        private enum YieldType {
            None,
            Text,
            List
        }
        private class Stack {
            private Stack<object> m_stack;

            internal Stack() {
                m_stack = new Stack<object>();
            }

            internal object Pop() {
                var top = Top;

                // vacuously deserialize remaining entity properties
                if (Top is RestXmlValue)
                    ((RestXmlValue)top).EndDeserialization();

                return m_stack.Pop();
            }
            internal void Push(object obj) {
                m_stack.Push(obj);
            }
            internal object Top {
                get { return m_stack.Peek(); }
            }
            internal RestXmlValue CurrentObject {
                get { return (RestXmlValue)m_stack.Peek(); }
            }
            internal ListDeserializer CurrentList {
                get { return (ListDeserializer)m_stack.Peek(); }
            }
            internal RestXmlSchema CurrentSchema {
                get { return m_stack.OfType<RestXmlValue>().First().Schema; }
            }
            internal bool IsEmpty {
                get { return m_stack.Count == 0; }
            }
        }
        private class TextDeserializer : TextReader {
            private readonly TextReader m_textReader;
            private readonly RestXmlDeserializer m_deserializer;
            private bool m_continued;

            internal TextDeserializer(TextReader textReader, RestXmlDeserializer deserializer) {
                m_textReader = textReader;
                m_deserializer = deserializer;
            }

            private async Task OnEndOfStream() {
                if (m_continued)
                    return;
                await m_deserializer.Continue();
            }

            public async override Task<int> ReadAsync(char[] buffer, int index, int count) {
                var result = await m_textReader.ReadAsync(buffer, index, count);
                if (count > 0 && result == 0)
                    await OnEndOfStream();
                return result;
            }
            public async override Task<string> ReadLineAsync() {
                var result = await m_textReader.ReadLineAsync();
                if (result == null)
                    await OnEndOfStream();
                return result;
            }
            public async override Task<string> ReadToEndAsync() {
                var result = await m_textReader.ReadToEndAsync();
                if (result == null)
                    await OnEndOfStream();
                return result;
            }

            public override int Read(char[] buffer, int index, int count) {
                var result = m_textReader.Read(buffer, index, count);
                if (count > 0 && result == 0)
                    OnEndOfStream().Wait();
                return result;
            }
            public override int Read() {
                var result = m_textReader.Read();
                if (result == -1)
                    OnEndOfStream().Wait();
                return result;
            }
            public override int Peek() {
                var result = m_textReader.Peek();
                if (result == -1)
                    OnEndOfStream().Wait();
                return result;
            }
            public override string ReadLine() {
                var result = m_textReader.ReadLine();
                if (result == null)
                    OnEndOfStream().Wait();
                return result;
            }
        }
        private class ListDeserializer : IStreamableAsync<object> {
            private enum State {
                BeforeGetEnumerator,
                Enumerating,
                Finished
            }

            private RestXmlDeserializer m_deserializer;
            private object m_current;
            private long m_position;
            private State m_state;

            internal ListDeserializer(RestXmlDeserializer deserializer) {
                m_deserializer = deserializer;
                m_position = 0;
                m_state = State.BeforeGetEnumerator;
            }
            internal void SetCurrent(object current) {
                m_position++;
                m_current = current;
            }

            public async Task<bool> MoveNextAsync() {
                if (m_state == State.BeforeGetEnumerator)
                    throw new InvalidOperationException();

                if (m_state == State.Finished)
                    return false;

                var position = m_position;

                // advance stream until a new element is generated
                while (m_position == position) {

                    if (!await m_deserializer.Continue()) {
                        m_state = State.Finished;
                        return false;
                    }
                } 

                return true;
            }
            public bool MoveNext() {
                return MoveNextAsync().Result;
            }

            public IEnumeratorAsync<object> GetEnumerator() {
                lock (this) {
                    if (m_state != State.BeforeGetEnumerator)
                        throw new InvalidOperationException(
                            "The stream cannot be enumerated more than once.");
                    m_state = State.Enumerating;
                }

                return (IEnumeratorAsync<object>)this;
            }
            IEnumerator<object> IEnumerable<object>.GetEnumerator() {
                return GetEnumerator();
            }
            IEnumeratorAsync IEnumerableAsync.GetEnumerator() {
                return GetEnumerator();
            }
            IEnumerator IEnumerable.GetEnumerator() {
                return GetEnumerator();
            }

            public object Current {
                get { return m_current; }
            }
            object IEnumerator.Current {
                get { return Current; }
            }
            public void Reset() {
                throw new NotSupportedException();
            }
            public void Dispose() {
                while (MoveNext())
                    continue;
            }
        }

        private readonly RestXmlLoader m_loader;
        private readonly XStreamReader m_stream;
        private readonly Stack m_stack;
        private YieldType m_yielded;

        internal RestXmlDeserializer(
            RestXmlLoader loader,
            XStreamReader stream) {

            m_loader = loader;
            m_stack = new Stack();
            m_stream = stream;
        }

        private async Task<object> DeserializePrimitiveElement(
            RestXmlType type, bool isStreamable = false) {
            ASSERT.Condition(type.IsSimpleType);

            var current = m_stream.Current;
            ASSERT.Condition(current.IsStartElement);

            // </a>
            if (current.IsEndElement)
                return null;

            // move to text/literal
            var moveNext = await m_stream.MoveNextAsync();
            ASSERT.Condition(moveNext);

            // assert we are not starting another element
            current = m_stream.Current;
            ASSERT.Condition(!current.IsStartElement);

            // <a></a>
            if (current.IsEndElement)
                return null;

            // get/return TextReader
            ASSERT.Condition(current.IsText);
            var textReader = current.Text;
            if (isStreamable) {
                m_yielded = YieldType.Text;
                return new TextDeserializer(textReader, this);
            }

            // read literal
            var literal = await current.Text.ReadToEndAsync();

            // deserialize literal
            var result = type.Deserialize(literal);

            // move to closing element tag
            moveNext = await m_stream.MoveNextAsync();
            ASSERT.Condition(moveNext);

            // assert we are closing the property element
            current = m_stream.Current;
            ASSERT.Condition(!current.IsStartElement);
            ASSERT.Condition(current.IsEndElement);

            return result;
        }
        private void DeserializeAttributes() {
            ASSERT.Condition(m_stream.Current.IsStartElement);

            var element = m_stream.Current.Element;
            ASSERT.Condition(element != null);

            // join element attributes with known properties by matching names
            foreach (var join in
                from a in element.Attributes()
                join p in m_stack.CurrentObject.RestXmlProperties() on a.Name equals p.XName
                select new {
                    Attribute = a,
                    Property = p
                }) {

                m_stack.CurrentObject.SetProperty(join.Property,
                    join.Property.PropertyType.Deserialize((string)join.Attribute));
            }
        }
        private async Task<object> DeserializeObjectElement(bool isListElement = false) {

            // advance to typed entity element or end element
            var moveNext = await m_stream.MoveNextAsync();
            ASSERT.Condition(moveNext);

            var current = m_stream.Current;
            ASSERT.Condition(current.IsElement);

            // no entity to deserialize; <a></a>
            if (!current.IsStartElement)
                return null;

            var element = current.Element;
            ASSERT.Condition(element != null);

            // do latebound resolution!
            var type = m_loader.GetType(element.Name);

            // ignore unknown object types
            if (type == null) {
                await m_stream.ConsumeSubtree();

                // try to get the next list element
                if (isListElement)
                    return await DeserializeObjectElement(isListElement);

                // <a></a> <==> <a><unknown/></a>
                return null;
            }

            if (type.IsSimpleType)
                return await DeserializePrimitiveElement(type);

            return await DeserializeComplexElement(type);
        }
        private async Task DeserializeListElement() {
            var currentList = m_stack.CurrentList;
            var listElement = await DeserializeObjectElement(isListElement: true);

            // yield list element
            if (listElement == null) {
                m_stack.Pop();
                return;
            }

            // yield new list element
            currentList.SetCurrent(listElement);
            m_yielded = YieldType.List;
        }
        private Task<object> DeserializeList() {
            var list = new ListDeserializer(this);

            m_stack.Push(list);

            // no list elements
            if (m_stream.Current.IsEndElement)
                m_stack.Pop();

            m_yielded = YieldType.List;
            return Task.FromResult<object>(list);
        }
        private async Task<object> DeserializeComplexElement(RestXmlType type) {
            var entity = new RestXmlValue(type.Schema, type);

            m_stack.Push(entity);

            // deserialize attributes
            DeserializeAttributes();

            // no property elements
            if (m_stream.Current.IsEndElement)
                m_stack.Pop();

            // deserialize elements
            else
                await DeserializeContent();

            return entity;
        }
        private Task<object> DeserializePropertyElement(RestXmlProperty property) {
            var type = property.PropertyType;

            if (type.IsSimpleType)
                return DeserializePrimitiveElement(type, property.IsStreamable);

            if (type.IsObject)
                return DeserializeObjectElement();

            if (type.IsList)
                return DeserializeList();

            return DeserializeComplexElement(type);
        }
        private async Task DeserializeContent() {
            var currentObject = m_stack.CurrentObject;
            var type = currentObject.Type;

            var contentProperty = type.ContentProperty;
            if (contentProperty != null) {

                var contentType = contentProperty.PropertyType;
                var content = await DeserializePrimitiveElement(contentType);
                currentObject.SetProperty(contentProperty, content);
            }

            else
                await DeserializeComplexElements();
        }
        private async Task DeserializeComplexElements() {

            // advance to next XStreamChunk
            while (await m_stream.MoveNextAsync()) {

                // get current XStreamChunk
                var current = m_stream.Current;

                // opening element => deserialize into a property
                if (current.IsStartElement) {

                    // get element representing property
                    var element = current.Element;
                    ASSERT.Condition(element != null);

                    var currentObject = m_stack.CurrentObject;

                    // attempt to join element name to a known property name
                    var property = currentObject.GetRestXmlProperty(element.Name);

                    // skip unknown elements
                    if (property == null) {
                        await m_stream.ConsumeSubtree();
                        continue;
                    }

                    // deserialize element
                    object value = await DeserializePropertyElement(property);

                    // assign deserialized value to property
                    currentObject.SetProperty(property, value);

                    if (m_yielded != YieldType.None)
                        return;

                // closing element => no more elements
                } 
                
                else if (current.IsEndElement)
                    break;
            }

            m_stack.Pop();
        }
        private async Task<bool> Continue() {
            if (m_stack.IsEmpty)
                return false;

            ASSERT.Condition(m_yielded != YieldType.None);

            // consume closeing element after reading text <text>some text</text>
            if (m_yielded == YieldType.Text) {
                await m_stream.MoveNextAsync();
                ASSERT.Condition(m_stream.Current.IsEndElement);
                ASSERT.Condition(!m_stream.Current.IsStartElement);
            }

            m_yielded = YieldType.None;

            ASSERT.Condition(m_yielded == YieldType.None);

            do {
                // stack empty before we discover the root schema
                var current = m_stack.Top;

                // next XStreamChunk is a new list element or end of the list
                if (current is ListDeserializer)
                    await DeserializeListElement();

                // next XStreamChunk is a new property or end of the entity
                else {
                    ASSERT.Condition(current is RestXmlValue);
                    await DeserializeContent();
                }

                if (m_yielded != YieldType.None)
                    break;

            } while (!m_stack.IsEmpty);

            return true;
        }

        internal async Task<object> Deserialize() {
            ASSERT.Condition(m_stack.IsEmpty);
            
            // advance to next XStreamChunk
            await m_stream.MoveNextAsync();

            // get current XStreamChunk
            var current = m_stream.Current;

            // get element representing property
            var element = current.Element;
            ASSERT.Condition(element != null);

            // get schema
            var xName = element.Name;
            var schema = m_loader.GetSchema(xName.Namespace);
            if (schema == null)
                throw new RestXmlSerializationException(
                    "Namespace '{0}' could not be resolved."
                    .Substitute(xName.Namespace)
                );

            // result
            object result = null;

            // type
            var type = m_loader.GetType(xName);
            if (type == null) {
                result = new RestXmlValue(schema, type);
                m_stack.Push(result);
            } 
            
            // message
            else
                result = await DeserializeComplexElement(type);

            return ((RestXmlValue)result).Value;
        }
    }
    internal class RestXmlSerializer {
        internal const int DefaultBufferSize = 1024 * 4;

        private struct Name {
            internal string LocalName;
            internal string Namespace;
            internal string Prefix;
        }

        private readonly int m_bufferSize;
        private readonly Lazy<char[]> m_buffer;
        private readonly XmlWriter m_xmlWriter;
        private readonly RestXmlSchema m_schema;
        private readonly Stack<RestXmlValue> m_stack;

        internal RestXmlSerializer(
            XmlWriter xmlWriter, 
            RestXmlSchema schema,
            int bufferSize = DefaultBufferSize) {

            m_xmlWriter = xmlWriter;
            m_schema = schema;
            m_stack = new Stack<RestXmlValue>();
            m_bufferSize = bufferSize;
            m_buffer = new Lazy<char[]>(() => new char[m_bufferSize]);
        }

        private bool IsNameQualified(XName xName) {

            return false;
        }
        private Name GetName(XName xName) {

            string ns = null;
            if (xName.Namespace != XNamespace.None)
                ns = xName.Namespace.ToString();

            string prefix = "";
            if (IsNameQualified(xName))
                prefix = m_schema.Prefixes(ns).FirstOrDefault();

            var localName = xName.LocalName;
            return new Name() {
                LocalName = localName,
                Namespace = ns,
                Prefix = prefix
            };
        }

        private async Task SerializeSimpleValue(RestXmlProperty property, object value) {

            var type = property.PropertyType;

            // content
            if (property.IsContent) {
                var serializedValue = type.Serialize(value);
                await m_xmlWriter.WriteStringAsync(serializedValue);
                return;
            }

            var propertyName = GetName(property.XName);

            // attribute
            if (property.IsAttribute) {
                if (!type.IsSimpleType)
                    throw new RestXmlSerializationException(
                        ("Property '{0}' on type '{1}' is of type '{2}' which is " +
                        "complex and so cannot be serialized as an attribute.")
                        .Substitute(propertyName, property.DeclaringType, type));

                var serializedValue = type.Serialize(value);
                await m_xmlWriter.WriteAttributeStringAsync(
                    prefix: propertyName.Prefix,
                    localName: propertyName.LocalName,
                    ns: propertyName.Namespace,
                    value: serializedValue
                );
            }

            // element
            else {

                // propertyName
                await m_xmlWriter.WriteStartElementAsync(
                    propertyName.Prefix, propertyName.LocalName, propertyName.Namespace);
                {
                    // stream
                    if (property.IsStreamable) {
                        var text = (TextReader)value;

                        // TextReader => XmlWriter
                        var read = 0;
                        var buffer = m_buffer.Value;
                        while ((read = await text.ReadBlockAsync(buffer, 0, m_bufferSize)) > 0)
                            await m_xmlWriter.WriteCharsAsync(buffer, 0, read);
                    }

                    // string
                    else {
                        var serializedValue = type.Serialize(value);
                        await m_xmlWriter.WriteStringAsync(serializedValue);
                    }
                }
                await m_xmlWriter.WriteEndElementAsync();
            }
        }
        private async Task SerializeValue(RestXmlProperty property, object value) {
            var type = property.PropertyType;

            // simple
            if (type.IsSimpleType)
                await SerializeSimpleValue(property, value);

            // complex
            else {

                var propertyName = GetName(property.XName);

                // property name
                await m_xmlWriter.WriteStartElementAsync(
                    propertyName.Prefix, propertyName.LocalName, propertyName.Namespace);
                {
                    // list value
                    if (type.IsList) {
                        var enumerable = (IEnumerable)value;
                        foreach (RestXmlValue element in enumerable)
                            await SerializeObject(element);
                    }

                    // object value
                    else if (type.IsObject)
                        await SerializeObject((RestXmlValue)value);

                    // complex value
                    else {
                        ASSERT.Condition(type.IsComplexType);
                        await SerializeProperties((RestXmlValue)value);
                    }

                }
                await m_xmlWriter.WriteEndElementAsync();
            }
        }
        private async Task SerializeProperty(RestXmlValue source, RestXmlProperty property) {
            
            // value
            var value = source.GetProperty(property);
            if (value == null) {

                if (property.IsOptional)
                    throw new RestXmlSerializationException("Missing member");

                return;
            }

            await SerializeValue(property, value);
        }

        internal async Task SerializeObject(RestXmlValue value) {
            var typeName = GetName(value.Type.XName);

            // type name
            await m_xmlWriter.WriteStartElementAsync(
                typeName.Prefix, typeName.LocalName, typeName.Namespace);
            {
                // properties
                await SerializeProperties(value);
            }
            await m_xmlWriter.WriteEndElementAsync();
        }
        internal async Task SerializeProperties(RestXmlValue source) {
            foreach (var property in source.RestXmlProperties())
                await SerializeProperty(source, property);
        }
    }
    internal struct RestXmlValue {
        private RestXmlSchema m_schema;
        private RestXmlType m_type;
        private object m_value;

        internal RestXmlValue(
            RestXmlSchema schema, 
            RestXmlType type = null,
            object value = null) {

            m_schema = schema;
            m_type = type;

            m_value = value;
            if (m_value == null) {

                if (m_type == null)
                    m_value = m_schema.Activate();

                else
                    m_value = m_type.Activate();
            }
        }

        internal object Value {
            get { return m_value; }
        }

        internal RestXmlSchema Schema {
            get { return m_schema; }
        }
        internal RestXmlType Type {
            get { return m_type; }
        }

        internal void SetProperty(RestXmlProperty property, object value) {
            ASSERT.Condition(property.DeclaringType == m_type);
            property.SetValue(m_value, value);
        }
        internal object GetProperty(RestXmlProperty property) {
            ASSERT.Condition(property.DeclaringType == m_type);
            var value = property.GetValue(m_value);

            if (value == null)
                return null;

            var type = property.PropertyType;
            if (type.IsObject)
                type = m_schema.Loader.GetType(value);

            if (type.IsComplexType)
                return new RestXmlValue(type.Schema, type, value);

            return value;
        }
        internal IEnumerable<RestXmlProperty> RestXmlProperties() {
            if (m_type != null)
                return m_type.Properties();

            return m_schema.Messages();
        }
        internal RestXmlProperty GetRestXmlProperty(XName xName) {
            if (m_type != null)
                return m_type.GetProperty(xName);

            return m_schema.GetMessage(xName);
        }
       
        internal void EndDeserialization() {
        }
    }

    public sealed class RestXmlSerializationException : Exception {
        public RestXmlSerializationException(string message)
            : base(message) {
        }
    }
}
namespace King.Rest.Reflection.Emit {

    public sealed class RestXmlSchemaFactory {
        private readonly RestXmlLoader m_loader;
        private readonly XsSchema m_xsSchema;

        internal RestXmlSchemaFactory(RestXmlLoader loader, XsSchema xsSchema) {
            m_loader = loader;
            m_xsSchema = xsSchema;
        }

        public RestXmlLoader Loader {
            get { return m_loader; }
        }
        public XsSchema XsSchema {
            get { return m_xsSchema; }
        }

        public RestXmlSchema CreateSchema(
            Func<RestXmlTypeFactory, RestXmlType> typeFactory = null,
            Func<object> activate = null) {
            
            return new RestXmlSchema(
                Loader, 
                XsSchema, 
                typeFactory,
                activate
            );
        }
    }
    public sealed class RestXmlTypeFactory {
        private readonly RestXmlSchema m_schema;
        private readonly XsType m_xsType;

        internal RestXmlTypeFactory(RestXmlSchema schema, XsType xsType) {
            m_schema = schema;
            m_xsType = xsType;
        }

        public XsType XsType {
            get { return m_xsType; }
        }
        public RestXmlLoader Loader {
            get { return Schema.Loader; }
        }
        public RestXmlSchema Schema {
            get { return m_schema; }
        }

        public RestXmlType CreateSimpleType(
            Func<string, object> deserialize = null,
            Func<object, string> serialize = null) {

            return new RestXmlSimpleType(
                Schema,
                m_xsType,
                deserialize,
                serialize
            );
        }
        public RestXmlType CreateComplexType(
            Func<object> activate = null,
            Func<RestXmlPropertyFactory, RestXmlProperty> propertyFactory = null) {

            if (propertyFactory == null)
                propertyFactory = o => o.CreateRestXmlProperty();

            return new RestXmlComplexType(
                Schema,
                m_xsType,
                activate,
                propertyFactory
            );
        }
    }
    public sealed class RestXmlPropertyFactory {
        private readonly RestXmlSchema m_schema;
        private readonly RestXmlType m_declaringType;
        private readonly XsObject m_xsObject;
        private readonly int? m_order;
        private readonly XName m_xName;

        internal RestXmlPropertyFactory(            
            RestXmlSchema schema,
            RestXmlType declaringType,
            XsObject xsObject = null,
            int? order = null) {

            m_schema = schema;
            m_declaringType = declaringType;
            m_xsObject = xsObject;
            m_order = order;

            var xsAttribute = xsObject as XsAttribute;
            if (xsAttribute != null)
                m_xName = xsAttribute.XName;

            var xsElement = xsObject as XsElement;
            if (xsElement != null)
                m_xName = xsElement.XName;
        }

        public RestXmlLoader Loader {
            get { return Schema.Loader; }
        }
        public RestXmlSchema Schema {
            get { return m_schema; }
        }
        public RestXmlType DeclaringType {
            get { return m_declaringType; }
        }
        public XsObject XsObject {
            get { return m_xsObject; }
        }
        public XsType XsType {
            get { return m_xsObject as XsType; }
        }
        public XsAttribute XsAttribute {
            get { return m_xsObject as XsAttribute; }
        }
        public XsElement XsElement {
            get { return m_xsObject as XsElement; }
        }
        public int? Order {
            get { return m_order; }
        }
        public string Name {
            get {
                if (XName == null)
                    return null;
                return XName.LocalName; 
            }
        }
        public XName XName {
            get { return m_xName; }
        }

        public RestXmlProperty CreateRestXmlProperty(
            Func<RestXmlTypeFactory, RestXmlType> simpleContent = null,
            Func<object, object> getValue = null,
            Action<object, object> setValue = null) {

            return new RestXmlProperty(
                schema: Schema, 
                xName: XName,
                xsObject: XsObject, 
                order: Order,
                simpleContent: simpleContent,
                declaringType: DeclaringType,
                getValue: getValue,
                setValue: setValue
            );
        }
    }
}